/*
 * Copyright 2012 Finbudget.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package finbudget.client.ui.table;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

import finbudget.client.general.PropertiesContainer;
import finbudget.client.general.SerializableTransferable;
import finbudget.client.ui.widget.upload.Attachment;

/**
 * This class represents a record shown in
 * a filtered table. The record might be a transaction from
 * TransLedger or a analytics from analytics etc.
 *
 */
public class Record extends PropertiesContainer implements SerializableTransferable {
	private static final long serialVersionUID = 1139242494116850039L;

	public final static String fieldKey = "recordkey";
	/**
	 * When record is created on a client it obtains first a surrogate key. The real
	 * key is provided after it is saved on the server. So as to identify these records
	 * server stores original surrogate key as fieldOriginalKey property.
	 */
	public static final String fieldOriginalKey = "originalkey";

	/**
	 * String representation of Key<?> of the current record.
	 */
	public String key;
	/**
	 * Container for properties.
	 */
	private HashMap<String, Serializable> values;
	/**
	 * This flag is used in client grids to mark
	 * records which were changed by a user.
	 */
	public boolean isChanged;
	/**
	 * This flag is used on the client to show
	 * the record should be deleted
	 */
	public boolean toDelete;
	/**
	 * Status of the object. It's shown in the
	 * corresponding field in the grid.
	 */
	public String status;

	private boolean readOnly;

	/** Serialization requirements */
	protected Record() {
	}
	
	public Record(String key) {
		this.key = key == null ? null : new String(key);
		isChanged = false;
		toDelete = false;
	}

	public String getKey() {
		return key;
	}

	@Override
	public Serializable getValue(String key) {
		if (fieldKey.equals(key)) {
			return this.key;
		}
		Serializable value = super.getValue(key);
		/*
		 * To support attachemnt's link to nodes we check if predefined
		 * field Attachment.keyNodeId is found then return it otherwise
		 * in case Attachment.keyNodeId is requested and it's not found
		 * assume node's key is stored in this.key field.
		 */
		if (value == null && Attachment.keyNodeId.equals(key)) {
			return this.key;
		}
		return value;
	}

	/**
	 * Creates a clone of the object.
	 * @return
	 */
	public Record makeClone() {
		Record dest = new Record(key);
		copy(this, dest);
		return dest;
	}

	protected static final void copy(Record source, Record dest) {
		if (source.values == null) {
			dest.values = null;
		} else {
			dest.createContainer();
			for (Entry<String, Serializable> item : source.values.entrySet()) {
				Serializable value = item.getValue();
				if (value == null) {
					dest.values.put(item.getKey(), null);
				} else if (value instanceof String) {
					String newValue = new String((String) value);
					dest.values.put(item.getKey(), newValue);
				} else {
					// In other cases just copy the reference.... 
					// TODO: another types are not supported in this version!!!!!!!!!!!!!!!!!!!!!!!!!!
					// The clue might be in using ClientSerializationStreamReader
					// http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java deserialize
					// or readObject from AbstractSerializationStreamReader
					dest.values.put(item.getKey(), value);
				}
			}
		}
		dest.isChanged = source.isChanged;
		dest.toDelete = source.toDelete;
		dest.status = source.status == null ? null : new String(source.status);
	}

	public Set<Entry<String, Serializable>> getValuesSet() {
		return values == null ? null : values.entrySet();
	}

	public HashMap<String, Serializable> getValues() {
		return values;
	}

	public void setValues(HashMap<String, Serializable> map) {
		values = map;
	}

	public void copyValues(HashMap<String, Serializable> map) {
		if (map == null) {
			values = null;
		} else {
			values = new HashMap<String, Serializable>(map.size());
			for (Entry<String, Serializable> entry : map.entrySet()) {
				values.put(entry.getKey(), entry.getValue());
			}
		}
	}

	public boolean isReadOnly() {
		return readOnly;
	}

	public void setReadOnly(boolean readOnly) {
		this.readOnly = readOnly;
	}

	@Override
	protected HashMap<String, Serializable> getContainer() {
		return values;
	}

	@Override
	protected void createContainer() {
		values = new HashMap<String, Serializable>();
	}

}
